import Where from './Where';

/**
 * @description: 格式化数据
 * @param {*} row
 * @param {*} key
 * @param {*} item
 * @param {*} isObject
 * @returns
 */
function formatWithOneData(row, key, item) {
	if (row[key] === void 0 || item.is_array) return row;
	// 如果是数组
	if (Array.isArray(item.bindFields)) {
		for (let field of item.bindFields) {
			if (typeof field === 'string') {
				row[field] = row[key][0] && row[key][0][field];
			}
		}
		Reflect.deleteProperty(row, key);
	} else if (this.isObject(item.bindFields)) {
		for (let [alias, value] of Object.entries(item.bindFields)) {
			row[alias] = row[key][0] && row[key][0][value];
		}
		Reflect.deleteProperty(row, key);
	} else {
		row[key] = row[key][0] || {};
	}
	return row;
}

// 格式化字段
function formatFieldData(target, field, isShow = 1) {
	if (typeof field == 'string') {
		if (field.includes(',')) {
			field.split(',').forEach((item) => {
				target[item] = isShow;
			});
		} else {
			target[field] = isShow;
		}
	} else if (Array.isArray(field)) {
		field.forEach((item) => {
			if (item.includes('.')) {
				item.split('.').forEach((item2) => {
					target[item2] = isShow;
				});
			} else {
				target[item] = isShow;
			}
		});
	}
}

// 代理service
export default function(modal) {
	//代理方法
	let proxyInstance = new Proxy(modal, {
		get(target, name) {
			const {
				$dx
			} = target;
			let whereInstance = new Where($dx);
			// 获取where key
			let whereKey = Symbol.for('dx-model-where');
			let whereOrKey = Symbol.for('dx-model-where-or');
			let fieldKey = Symbol.for('dx-model-field');
			let orderKey = Symbol.for('dx-model-order');
			let limitKey = Symbol.for('dx-model-limit');
			let skipKey = Symbol.for('dx-model-skip');
			let withKey = Symbol.for('dx-model-with');
			//当前实例挂在where
			if (target[whereKey] === void 0) target[whereKey] = [];
			if (target[whereOrKey] === void 0) target[whereOrKey] = [];
			if (target[fieldKey] === void 0) target[fieldKey] = {};
			if (target[withKey] === void 0) target[withKey] = {};
			if (target[orderKey] === void 0) target[orderKey] = {};
			if (target[limitKey] === void 0) target[limitKey] = 0;
			if (target[skipKey] === void 0) target[skipKey] = 0;

			// 拦截处理
			switch (name) {
				case 'where':
					return function(...args) {
						target[whereKey].push(args);
						return proxyInstance;
					};
				case 'whereOr':
					return function(...args) {
						target[whereOrKey].push(args);
						return proxyInstance;
					};
				case 'getQueryWhere':
					return function(...args) {
						return whereInstance.getWhere(args);
					};
				case 'field':
					return function(key) {
						formatFieldData(target[fieldKey], key);
						return proxyInstance;
					};
				case 'hidden':
					return function(key) {
						formatFieldData(target[fieldKey], key, 0);
						return proxyInstance;
					};
				case 'order':
					return function(key, value) {
						if (!key || !value || !['asc', 'desc'].includes(value.toLowerCase())) {
							throw new Error('orderby parameter error');
						}
						target[orderKey][key] = value.toLowerCase();
						return proxyInstance;
					};
				case 'limit':
					return function(num) {
						num = Number(num);
						if (isNaN(num)) throw new Error('limit parameter error');
						target[limitKey] = num;
						return proxyInstance;
					};
				case 'skip':
					return function(num) {
						num = Number(num);
						if (isNaN(num)) throw new Error('skip parameter error');
						target[skipKey] = num;
						return proxyInstance;
					};
				case 'insert':
					//单行插入
					return async function(row, autoWrite = true) {
						if (!$dx.utils.isObject(row)) throw new Error(
							'incorrect data format inserted');
						//设置数据对象
						let data = await target.setData(row, autoWrite);
						//  插入之前
						let result = await proxyInstance.$emit('before_insert', data);
						if (result === false) return result;
						if (result !== void 0) data = result;
						// 确保没有_id
						Reflect.deleteProperty(data, '_id');
						// 插入数据
						const {
							id
						} = await $dx.db.collection(target.entity.table).add(data);
						if (id) {
							data._id = id; //赋值_id
							await proxyInstance.$emit('after_insert', data);
						}
						return id;
					};
				case 'insertAll':
					// 批量插入
					return async function(list, autoWrite = true) {
						if (!$dx.utils.isArray(list)) throw new Error(
							'incorrect data format inserted');
						// 处理数据
						let newList = [];
						for (let item of list) {
							let row = await target.setData(item, autoWrite);
							// 插入之前
							await proxyInstance.$emit('before_insert', row);
							Reflect.deleteProperty(row, '_id');
							newList.push({
								...row,
							});
						}
						//  插入之前
						let result = await proxyInstance.$emit('before_insert', newList);
						if (result === false) return result;
						if (result !== void 0) newList = result;
						//插入
						const {
							ids
						} = await $dx.db.collection(target.entity.table).add(newList);
						// 插入之后
						if (ids) {
							await proxyInstance.$emit(
								'after_insert',
								newList.map((item, index) => {
									item._id = ids[index];
									return item;
								})
							);
						}
						return ids;
					};
				case 'delete':
					// 删除
					return async function() {
						let whereParam = whereInstance.toGetWhere(target[whereKey]);
						let whereOrParam = whereInstance.toGetWhere(target[whereOrKey]);
						const _ids =
							(whereParam &&
								whereParam[0] &&
								whereParam[0]._id &&
								whereParam[0]._id.operands) || [];
						let status = await proxyInstance.$emit('before_delete',_ids);
						if (status === false) return;
						// 组装where

						// 执行删除
						let {
							deleted
						} = await $dx.db
							.collection(target.entity.table)
							.where($dx.db.command.or(...whereOrParam, $dx.db.command.and(...
								whereParam)))
							.remove();
						if (deleted) proxyInstance.$emit('after_delete',_ids);
						return deleted;
					};
				case 'deleteAll':
					return async function() {
						return await $dx.db
							.collection(target.entity.table)
							.where({
								_id: $dx.db.command.exists(true),
							})
							.remove();
					};
				case 'update':
					//更新
					return async function(row, autoWrite = false) {
						if (!$dx.utils.isObject(row)) throw new Error(
							'incorrect data format updated');
						//设置数据对象
						let data = await target.setData(row, autoWrite);
						let hasId = row._id !== void 0 && row._id;
						if (hasId) {
							data._id = row._id;
						}
						//更新之前
						let result = await proxyInstance.$emit('before_update', data);
						if (result === false) return result;
						if (result !== void 0) data = result;
						// 确保没有_id
						Reflect.deleteProperty(data, '_id');
						// 组装where
						let whereParam = whereInstance.toGetWhere(target[whereKey]);
						let whereOrParam = whereInstance.toGetWhere(target[whereOrKey]);
						let status = false;
						// 自动写入时间
						if (
							typeof target.update_time === 'string' &&
							target.update_time &&
							target.autoWriteTime === true &&
							!data[target.update_time] &&
							target.entity.fields.some((item) => item.key === target.update_time)
						) {
							data[target.update_time] = new Date().getTime();
						}
						// 有_id更新
						if (hasId) {
							// 有 _id ，按_id 改
							let {
								updated
							} = await $dx.db.collection(target.entity.table).doc(row._id).update(
								data);
							if (updated) status = true;
						} else {
							const {
								updated
							} = await $dx.db
								.collection(target.entity.table)
								.where($dx.db.command.or(...whereOrParam, $dx.db.command.and(...
									whereParam)))
								.update(data);
							if (updated) status = true;
						}
						target[whereKey].length = 0;
						target[whereOrKey].length = 0;
						if (status) {
							await proxyInstance.$emit('after_update', {
								...data,
								_id: row._id
							});
						}
						return status;
					};
				case 'save':
					return async function(row, autoWrite = true) {
						if (!$dx.utils.isObject(row)) throw new Error('incorrect data format save');
						// 更新，没有值的字段不需要自动写入
						if (row._id && autoWrite) {
							autoWrite = false;
						}
						//设置数据对象
						let data = await target.setData(row, autoWrite);
						//save之前
						let result = await proxyInstance.$emit('before_write', data);
						if (result === false) return result;
						if (result !== void 0) data = result;
						let status = false;
						// 没有_id ，增 或 改
						if (!row._id) {
							if (target[whereKey].length) {
								// 自动写入时间
								if (
									typeof target.update_time === 'string' &&
									target.update_time &&
									target.autoWriteTime === true &&
									!data[target.update_time] &&
									target.entity.fields.some((item) => item.key === target
										.update_time)
								) {
									data[target.update_time] = new Date().getTime();
								}
								// 组装where
								let whereParam = whereInstance.toGetWhere(target[whereKey]);
								let whereOrParam = whereInstance.toGetWhere(target[whereOrKey]);
								let {
									updated
								} = await $dx.db
									.collection(target.entity.table)
									.where($dx.db.command.or(...whereOrParam, $dx.db.command.and(...
										whereParam)))
									.update(data);
								target[whereKey].length = 0;
								target[whereOrKey].length = 0;
								if (updated) status = true;
							} else {
								let {
									id
								} = await $dx.db.collection(target.entity.table).add(data);
								if (!!id) status = true;
							}
						} else {
							// 自动写入时间
							if (
								typeof target.update_time === 'string' &&
								target.update_time &&
								target.autoWriteTime === true &&
								!data[target.update_time] &&
								target.entity.fields.some((item) => item.key === target.update_time)
							) {
								data[target.update_time] = new Date().getTime();
							}
							// 有 _id ，按_id 改
							let {
								updated
							} = await $dx.db.collection(target.entity.table).doc(row._id).update(
								data);
							if (updated) status = true;
						}
						if (status) await proxyInstance.$emit('after_write', data);
						return status;
					};
				case 'select':
					return async function(isOrigin = false) {
						// 组装where
						let whereParam = whereInstance.toGetWhere(target[whereKey]);
						let whereOrParam = whereInstance.toGetWhere(target[whereOrKey]);
						if (target.soft_delete) {
							whereParam.push({
								[target.delete_time]: $dx.db.command.or(
									$dx.db.command.eq(null),
									$dx.db.command.eq(''),
									$dx.db.command.eq(0),
									$dx.db.command.eq(false)
								),
							});
						}
						// 开始查询
						let entity = $dx.db.collection(target.entity.table);
						let withModels = Object.keys(target[withKey]);
						let f = 'get';
						// 是否有关联
						if (!withModels.length) {
							//指定是否返回某字段
							if (!$dx.utils.isEmpty(target[fieldKey])) {
								entity = entity.field(target[fieldKey]);
							}
							//条件查询
							if (whereParam.length && whereOrParam.length) {
								entity = entity.where($dx.db.command.or(...whereParam, $dx.db
									.command.and(...whereParam)));
							} else if (whereParam.length) {
								entity = entity.where($dx.db.command.and(...whereParam));
							}
							// 排序字段
							for (let [key, value] of Object.entries(target[orderKey])) {
								entity = entity.orderBy(key, value);
							}
						} else {
							f = 'end';
							entity = entity.aggregate();
							// 条件查询
							if (whereParam.length && whereOrParam.length) {
								entity = entity.match($dx.db.command.or(...whereParam, $dx.db
									.command.and(...whereParam)));
							} else if (whereParam.length) {
								entity = entity.match($dx.db.command.and(...whereParam));
							}
							const dbCmd = $dx.db.command;
							const $ = dbCmd.aggregate;
							// 关联查询
							for (let key of withModels) {
								let item = target[withKey][key];
								let pipeline = $.pipeline();
								let childWhereParam;
								// 组装where
								// 取字段，是否是数组
								if (item.is_array) {
									childWhereParam = dbCmd.expr($.in([`$${item.foreignKey}`,
										`$$${item.localKeyAlias}`
									]));
								} else {
									childWhereParam = dbCmd.expr($.eq([`$${item.foreignKey}`,
										`$$${item.localKeyAlias}`
									]));
								}
								pipeline = pipeline.match(childWhereParam);
								// 指定是否返回某字段
								if (!$dx.utils.isEmpty(item.field)) {
									pipeline = pipeline.project(item.field);
								}
								// 限制数量
								if (item.type === 'hasMany') {
									if (item.limit) {
										pipeline = pipeline.limit(item.limit);
									}
								} else {
									pipeline = pipeline.limit(item.limit);
								}
								entity = entity.lookup({
									from: item.model,
									let: {
										[item.localKeyAlias]: `$${item.localKey}`
									},
									pipeline: pipeline.done(),
									as: key,
								});
							}
							// 指定是否返回某字段
							if (!$dx.utils.isEmpty(target[fieldKey])) {
								entity = entity.project(target[fieldKey]);
							}
							// 排序字段
							if (!$dx.utils.isEmpty(target[orderKey])) {
								let order = {};
								for (let [key, value] of Object.entries(target[orderKey])) {
									order[key] = value === 'asc' ? 1 : -1;
								}
								entity = entity.sort(order);
							}
						}
						// 跳过
						if (target[skipKey]) {
							entity = entity.skip(target[skipKey]);
						}
						// 限制数量
						if (target[limitKey]) {
							entity = entity.limit(target[limitKey]);
						}
						// 查询
						let {
							affectedDocs,
							data
						} = await entity[f]();
						target[whereKey].length = 0;
						target[whereOrKey].length = 0;
						if (affectedDocs > 0) {
							return await Promise.all(
								data.map(async (row) => {
									for (let key of withModels) {
										let item = target[withKey][key];
										if (item.type === 'hasOne') {
											row = formatWithOneData.call($dx.utils, row,
												key, item);
										}
									}
									if (isOrigin) {
										return row;
									}
									return await target.getData(row);
								})
							);
						}
						return [];
					};
				case 'count':
					// 组装where
					let whereParam = whereInstance.toGetWhere(target[whereKey]);
					let whereOrParam = whereInstance.toGetWhere(target[whereOrKey]);
					//统计数量
					return async function() {
						let entity = $dx.db
							.collection(target.entity.table);
						if (whereParam.length && whereOrParam.length) {
							entity = entity.where($dx.db.command.or(...whereOrParam, $dx.db.command
								.and(...whereParam)));
						} else if (whereParam.length) {
							entity = entity.where($dx.db.command.and(...whereParam));
						} else if (whereOrParam.length) {
							entity = entity.where($dx.db.command.or(...whereOrParam));
						}
						let {
							total
						} = await entity.count();
						target[whereKey].length = 0;
						target[whereOrKey].length = 0;
						return total || 0;
					};
				case 'find':
					// 单行查询
					return async function(isOrigin = false) {
						let data = await proxyInstance.select(isOrigin);
						return data[0] || null;
					};
				case 'setInc':
					return async function(field, num = 1) {
						// 组装where
						let whereParam = whereInstance.toGetWhere(target[whereKey]);
						let whereOrParam = whereInstance.toGetWhere(target[whereOrKey]);
						// 组装更新数据
						let updateData = {};
						updateData[field] = $dx.db.command.inc(num);
						// 更新数据
						let {
							updated
						} = await $dx.db
							.collection(target.entity.table)
							.where($dx.db.command.or(...whereOrParam, $dx.db.command.and(...
								whereParam)))
							.update(updateData);
						return updated;
					};
				case 'setDec':
					return function(field, num = 1) {
						if (num > 0) {
							num = -num;
						} else {
							num = Math.abs(num);
						}
						return proxyInstance.setInc(field, num);
					};
				case 'with':
					return function(withData) {
						if (typeof withData == 'string') {
							target[withKey][withData] = proxyInstance[withData]().getParam();
						} else if (Array.isArray(withData)) {
							for (let item of withData) {
								target[withKey][item] = proxyInstance[item]().getParam();
							}
						}
						return proxyInstance;
					};
				case 'hasOne':
					return function(model, localKey, foreignKey, is_array = false, limit = 1) {
						// 一对一关联
						let hasOneProxy = new Proxy({
							type: 'hasOne',
							model,
							foreignKey,
							localKey,
							limit,
							is_array,
							localKeyAlias: `dx${localKey}`, //如果是_id，不允许，故加前缀
							field: {},
						}, {
							get(oneTar, name) {
								switch (name) {
									case 'bindField':
										return function(field) {
											if (Array.isArray(field) && field.length) {
												oneTar.bindFields = field;
											} else if (typeof field == 'string') {
												oneTar.bindFields = field.includes(
													',') ? field.split(',') : [field];
											} else if ($dx.utils.isObject(field)) {
												oneTar.bindFields = field;
											}
											return hasOneProxy;
										};
									case 'field':
										return function(field) {
											formatFieldData(oneTar.field, field);
											return hasOneProxy;
										};
									case 'hidden':
										return function(field) {
											formatFieldData(oneTar.field, field, 0);
											return hasOneProxy;
										};
									case 'getParam':
										return function() {
											return oneTar;
										};
								}
							},
						});
						return hasOneProxy;
					};
				case 'hasMany':
					return function(model, localKey, foreignKey, limit = 0) {
						let hasManyProxy = new Proxy({
							type: 'hasMany',
							model,
							foreignKey,
							localKey,
							localKeyAlias: `dx${localKey}`,
							field: {},
							limit,
						}, {
							get(manyTar, name) {
								switch (name) {
									case 'field':
										return function(field) {
											formatFieldData(manyTar.field, field);
											return hasManyProxy;
										};
									case 'hidden':
										return function(field) {
											formatFieldData(manyTar.field, field, 0);
											return hasManyProxy;
										};
									case 'limit':
										return function(limit) {
											limit = parseInt(limit);
											if (isNaN(limit)) {
												limit = 0;
											}
											manyTar.limit = limit;
											return hasManyProxy;
										};
									case 'getParam':
										return function() {
											return manyTar;
										};
								}
							},
						});
						// 一对d多关联
						return hasManyProxy;
					};
				case 'model':
					return $dx.db.collection(target.entity.table);
				default:
					return target[name];
			}
		},
		set() {},
	});
	return proxyInstance;
}